
#include <BFont>
#include <BPainter>
#include <BMouseEvent>
#include <BSystem>
#include <BStyle>

#include <BListWidget>
#include "member_BListItem.h"
#include "member_BListWidget.h"

using namespace BWE;

#define member					(*(member_BListWidget*)_ptr)
#define member_allocate()		_ptr = new member_BListWidget(this)
#define member_release()		delete (member_BListWidget*)_ptr

BListWidget::BListWidget(BWidget* parent) : BScrollArea(parent)
{
	member_allocate();
}
BListWidget::~BListWidget()
{
	member_release();
}

bool BListWidget::addItem(BListItem* item)
{
	if (item && !member.items.contain(item))
	{
		member.items.append(item);
		if (item_member(item)->list != this)
		{
			if (item_member(item)->list)
				item_member(item)->list->removeItem(item);
			item_member(item)->list = this;
		}
		if (item_member(item)->widget)
			this->addChild(item_member(item)->widget);
		member.dirty = true;
		this->fresh();
		return true;
	}
	return false;
}
BListItem* BListWidget::addItem(const BString& text)
{
	BListItem* item = new BListItem(text);
	this->addItem(item);
	return item;
}
BListItem* BListWidget::addItem(Graph graph, const BString& text)
{
	BListItem* item = new BListItem(graph, text);
	this->addItem(item);
	return item;
}
BListItem* BListWidget::addItem(BIcon* icon, const BString& text)
{
	BListItem* item = new BListItem(icon, text);
	this->addItem(item);
	return item;
}
BListItem* BListWidget::addItem(BImage* image, const BString& text)
{
	BListItem* item = new BListItem(image, text);
	this->addItem(item);
	return item;
}
BListItem* BListWidget::addItem(BWidget* widget, const BString& text)
{
	BListItem* item = new BListItem(widget, text);
	this->addItem(item);
	return item;
}

bool BListWidget::insertItem(int pos, BListItem* item)
{
	if (!item || pos < 0 || pos > member.items.size())
		return false;
	if (member.items.contain(item))
	{
		int index = member.items.index(item);
		return member.items.move(index, pos);
		return true;
	}
	member.items.insert(pos, item);
	if (item_member(item)->list != this)
	{
		if (item_member(item)->list)
			item_member(item)->list->removeItem(item);
		item_member(item)->list = this;
	}
	member.dirty = true;
	this->fresh();
	return true;
}
BListItem* BListWidget::insertItem(int pos, const BString& text)
{
	if (pos < 0 || pos > member.items.size())
		return 0;
	BListItemHolder item = new BListItem(text);
	if (insertItem(pos, item))
		return item;
	return 0;
}
BListItem* BListWidget::insertItem(int pos, Graph graph, const BString& text)
{
	if (pos < 0 || pos > member.items.size())
		return 0;
	BListItemHolder item = new BListItem(graph, text);
	if (insertItem(pos, item))
		return item;
	return 0;
}
BListItem* BListWidget::insertItem(int pos, BIcon* icon, const BString& text)
{
	if (pos < 0 || pos > member.items.size())
		return 0;
	BListItemHolder item = new BListItem(icon, text);
	if (insertItem(pos, item))
		return item;
	return 0;
}
BListItem* BListWidget::insertItem(int pos, BImage* image, const BString& text)
{
	if (pos < 0 || pos > member.items.size())
		return 0;
	BListItemHolder item = new BListItem(image, text);
	if (insertItem(pos, item))
		return item;
	return 0;
}
BListItem* BListWidget::insertItem(int pos, BWidget* widget, const BString& text)
{
	if (pos < 0 || pos > member.items.size())
		return 0;
	BListItemHolder item = new BListItem(widget, text);
	if (insertItem(pos, item))
		return item;
	return 0;
}

bool BListWidget::removeItem(int index)
{
	BListItem* item = member.items(index);
	return removeItem(item);
}
bool BListWidget::removeItem(BListItem* item)
{
	if (member.items.contain(item))
	{
		if (member.hoveredItem == item)
			member.hoveredItem = 0;
		if (member.pressedItem == item)
			member.pressedItem = 0;
		if (item_member(item)->widget)
			this->removeChild(item_member(item)->widget);
		member.selectedItems.remove(item);
		item_member(item)->list = 0;
		member.items.remove(item);
		member.dirty = true;
		this->fresh();
		return true;
	}
	return false;
}
bool BListWidget::removeItem(const BString& text)
{
	BListItem* item = this->item(text);
	return removeItem(item);
}

bool BListWidget::clearSelection()
{
	if (member.selectedItems.size())
	{
		while (BListItem* item = member.selectedItems.last())
		{
			item_member(item)->selected = false;
			member.selectedItems -= item;
			emit(Signal_ItemSelected, item);
		}
		emit(Signal_SelectionChanged);
		this->fresh();
	}
	return true;
}
void BListWidget::clearItems()
{
	clearSelection();
	for (BListItem* item : member.items)
	{
		item_member(item)->list = 0;
	}
	member.items.clear();
	member.hoveredItem = 0;
	member.showItemBegin = -1;
	member.showItemEnd = -1;
	this->fresh();
}

void BListWidget::setSelectMode(SelectMode selectMode)
{
	if (member.selectMode != selectMode)
	{
		member.selectMode = selectMode;
		this->clearSelection();
		this->fresh();
	}
}
SelectMode BListWidget::selectMode() const
{
	return member.selectMode;
}

bool BListWidget::setItemSelected(BListItem* item, bool selected)
{
	if (!item)
		return false;
	if (selected)
	{
		if (!member.selectedItems.contain(item))
		{
			if (member.selectMode & Select_Single)
				clearSelection();
			member.selectedItems.append(item);
			item_member(item)->selected = true;
			emit(Signal_ItemSelected, item);
			emit(Signal_SelectionChanged);
			this->fresh();
		}
	}
	else
	{
		if (member.selectedItems.remove(item))
		{
			item_member(item)->selected = false;
			emit(Signal_ItemSelected, item);
			emit(Signal_SelectionChanged);
			this->fresh();
		}
	}
	return true;
}
bool BListWidget::setSelection(const BListItemArray& selection)
{
	bool selectionChanged = false;
	for (int i = 0; i < member.selectedItems.size(); i++)
	{
		BListItem* item = member.selectedItems[i];
		if (!selection.contain(item))
		{
			item_member(item)->selected = false;
			member.selectedItems.remove(i--);
			emit(Signal_ItemSelected, item);
			selectionChanged = true;
		}
	}
	for (int i = 0; i < selection.size(); i++)
	{
		BListItem* item = selection[i];
		if (!member.selectedItems.contain(item))
		{
			item_member(item)->selected = true;
			member.selectedItems.append(item);
			emit(Signal_ItemSelected, item);
			selectionChanged = true;
		}
	}
	member.selectedItems = selection;
	if (selectionChanged)
	{
		emit(Signal_SelectionChanged);
		this->fresh();
		return true;
	}
	return false;
}
const BListItemArray& BListWidget::selection() const
{
	return member.selectedItems;
}

int BListWidget::selectedItemCount() const
{
	return member.selectedItems.size();
}
BListItem* BListWidget::selectedItem(int index)
{
	return member.selectedItems(index);
}
const BListItem* BListWidget::selectedItem(int index) const
{
	return member.selectedItems(index);
}

int BListWidget::itemCount() const
{
	return member.items.size();
}
BListItem* BListWidget::item(int index)
{
	return member.items(index);
}
const BListItem* BListWidget::item(int index) const
{
	return member.items(index);
}

BListItem* BListWidget::item(const BString& text)
{
	for (auto item : member.items)
	{
		if (item->text() == text)
			return item;
	}
	return 0;
}
const BListItem* BListWidget::item(const BString& text) const
{
	for (auto item : member.items)
	{
		if (item->text() == text)
			return item;
	}
	return 0;
}

BListItem* BListWidget::itemAt(const BPoint& point)
{
	if (member.items.empty())
		return 0;
	if (member.showItemBegin < 0)
		return 0;
	const BRect& crect = this->centerRect();
	BPoint offset = this->area().pos();
	for(int i = member.showItemBegin; i <= member.showItemEnd; i++)
	{
		BListItem* item = member.items[i].ptr();
		if (!item_member(item)->visible)
			continue;
		BRect abRect = item_member(item)->rect + offset;
		abRect.right() = crect.right();
		if (abRect.contain(point))
			return item;
	}
	return 0;
}
const BListItem* BListWidget::itemAt(const BPoint& point) const
{
	if (member.items.empty())
		return 0;
	if (member.showItemBegin < 0)
		return 0;
	BPoint offset = this->area().pos();
	for (int i = member.showItemBegin; i <= member.showItemEnd; i++)
	{
		BListItem* item = member.items[i].ptr();
		if (!item_member(item)->visible)
			continue;
		BRect abRect = item_member(item)->rect + offset;
		if (abRect.contain(point))
			return item;
	}
	return 0;
}

BListItem* BListWidget::itemOfName(const BString& name)
{
	if (name.empty())
		return 0;
	for (int i = 0; i < member.items.size(); i++)
	{
		BListItem* item = member.items[i].ptr();
		if (item->name() == name)
		{
			return item;
		}
	}
	return 0;
}
const BListItem* BListWidget::itemOfName(const BString& name) const
{
	if (name.empty())
		return 0;
	for (int i = 0; i < member.items.size(); i++)
	{
		const BListItem* item = member.items[i].ptr();
		if (item->name() == name)
		{
			return item;
		}
	}
	return 0;
}

BListItem* BListWidget::itemOfAsset(const BValue& asset)
{
	if (asset.empty())
		return 0;
	for (int i = 0; i < member.items.size(); i++)
	{
		BListItem* item = member.items[i].ptr();
		if (item->asset() == asset)
		{
			return item;
		}
	}
	return 0;
}
const BListItem* BListWidget::itemOfAsset(const BValue& asset) const
{
	if (asset.empty())
		return 0;
	for (int i = 0; i < member.items.size(); i++)
	{
		const BListItem* item = member.items[i].ptr();
		if (item->asset() == asset)
		{
			return item;
		}
	}
	return 0;
}

BListItem* BListWidget::itemOfWidget(const BWidget* widget)
{
	if (!widget)
		return 0;
	for (int i = 0; i < member.items.size(); i++)
	{
		BListItem* item = member.items[i].ptr();
		if (item->widget() == widget)
		{
			return item;
		}
	}
	return 0;
}
const BListItem* BListWidget::itemOfWidget(const BWidget* widget) const
{
	if (!widget)
		return 0;
	for (int i = 0; i < member.items.size(); i++)
	{
		const BListItem* item = member.items[i].ptr();
		if (item->widget() == widget)
		{
			return item;
		}
	}
	return 0;
}

bool BListWidget::scrollToItem(BListItem* item, Align align)
{
	if (!item || item_member(item)->list != this)
		return false;
	if (member.dirty)
	{
		member.freshArea();
	}
	BRect crect = this->centerRect();
	BRect area = this->area();
	if (item)
	{
		BRect rect = item->rect() + area.pos();
		BRect rect1 = crect.align(align, rect.size());
		BPoint offset = rect1.pos() - rect.pos();
		area.move(offset);
	}
	else
	{
		area = crect.align(align, area.size());
	}
	this->setArea(area);
	return true;
}
bool BListWidget::sortItems(int direction)
{
	if (direction == 0)
		return false;
	while (1)
	{
		bool changed = false;
		for (auto it = member.items.begin(); it.index() < member.items.size() - 1; ++it)
		{
			auto next = it.next();
			BListItem* item0 = *it;
			BListItem* item1 = *it.next();
			if (direction > 0 && item0->text() > item1->text())
			{
				member.items.swap(it, next);
				changed = true;
			}
			if (direction < 0 && item0->text() < item1->text())
			{
				member.items.swap(it, next);
				changed = true;
			}
		}
		if (!changed)
			break;
	}
	return true;
}

bool BListWidget::query(const BString& text, BValue& value) const
{
	if (text == "visual-items")
	{
		BListItemArray& items = value.fill<BListItemArray>();
		items.reset();
		for (auto it = member.items.seek(member.showItemBegin); it.index() <= member.showItemEnd; ++it)
		{
			items.append(it->ptr());
		}
		return true;
	}
	return BScrollArea::query(text, value);
}

void BListWidget::scrollArea()
{
	member.dirty = true;
	this->fresh();
	BScrollArea::scrollArea();
}

void BListWidget::resizeEvent(const BEvent& event)
{
	BScrollArea::resizeEvent(event);
	member.dirty = true;
}

void BListWidget::styleEvent(const BEvent& event)
{
	BScrollArea::styleEvent(event);
	for (int i = 0; i < member.items.size(); i++)
	{
		BListItem* item = member.items[i];
		item_member(item)->dirty = true;
	}
	if (const BStyle* style = this->realStyle())
	{
		if (const BFont* font = style->font())
		{
			this->verticalScrollBar()->setStep(font->size());
		}
		member.iconSize = style->value(Value_Icon_Size);
		member.dirty = true;
	}
	this->fresh();
}
void BListWidget::freshEvent(const BEvent& event)
{
	BScrollArea::freshEvent(event);
	if (member.dirty)
	{
		member.freshArea();
		member.freshVisualItems();
		member.dirty = false;
	}
}
void BListWidget::paintEvent(const BEvent& event)
{
	BScrollArea::paintEvent(event);
	BPainter painter(this);

	const BRect& crect = this->centerRect();
	painter.setClip(this->centerRect());
	if (member.showItemBegin >= 0)
	{
		BPoint offset = this->area().pos();
		for (int i = member.showItemBegin; i <= member.showItemEnd; i++)
		{
			const BListItem* item = member.items[i];
			BRect abRect = item_member(item)->rect + offset;
			abRect.right() = crect.right();
			StyleRole itemColorRole = Color_Item;
			if (item->isSelected())
				itemColorRole = Color_Item_Checked;
			else if (member.hoveredItem == item)
				itemColorRole = Color_Item_Hovered;
			else if (this->actived())
				itemColorRole = Color_Item_Actived;
			if (painter.setColor(itemColorRole))
				painter.fillRect(abRect);

			BImage* image = item_member(item)->image;
			if (BIcon* icon = item_member(item)->icon)
			{
				if (item_member(item)->selected)
					image = icon->image(State_Selected);
				else if (member.hoveredItem == item)
					image = icon->image(State_Hovered);
				else if (this->actived())
					image = icon->image(State_Actived);
				else
					image = icon->image(State_Normal);
			}
			if (image)
			{
				BRect imageRect = item_member(item)->ia_imageRect + offset;
				painter.setColor(item_member(item)->color);
				painter.drawImage(imageRect, image);
			}
			else
			{
				if (Graph graph = item_member(item)->graph)
				{
					BRect imageRect = item_member(item)->ia_imageRect + offset;
					if (painter.setColor(item_member(item)->color))
						painter.drawGraph(imageRect, graph);
				}
			}
			if (painter.setColor(Color_Text))
			{
				BRect textRect = item_member(item)->ia_textRect + offset;
				painter.drawText(textRect, item->text());
			}
		}
	}
	painter.setClip(this->size());
}

void BListWidget::mouseEnterEvent(const BMouseEvent& mouseEvent)
{
	member.hoveredItem	= 0;
	BScrollArea::mouseEnterEvent(mouseEvent);
}
void BListWidget::mouseLeaveEvent(const BMouseEvent& mouseEvent)
{
	if (member.hoveredItem != 0)
	{
		member.hoveredItem = 0;
		fresh();
	}
	BScrollArea::mouseLeaveEvent(mouseEvent);
}

void BListWidget::mousePressEvent(const BMouseEvent& mouseEvent)
{
	const BPoint& pos = mouseEvent.pos();
	if (member.pressedItem != member.hoveredItem)
	{
		member.pressedItem = member.hoveredItem;
		this->fresh();
	}
	if (member.selectMode & Select_Multiple)
	{
		BListItem* item = member.hoveredItem;
		if (mouseEvent.keyState(Key_LCtrl))
		{
			setItemSelected(item, !item_member(item)->selected);
		}
		else if (mouseEvent.keyState(Key_LShift))
		{
			if (member.selectedItems.empty())
			{
				setItemSelected(item, true);
			}
			else
			{
				if (item)
				{
					BListItem* firstItem = member.selectedItems.first();
					int index0 = member.items.index(firstItem);
					int index1 = member.items.index(item);
					int min = bMin(index0, index1);
					int max = bMax(index0, index1);
					for (int i = 0; i < member.selectedItems.size(); i++)
					{
						BListItem* sitem = member.selectedItems[i];
						int index = member.items.index(sitem);
						if (index < min || index > max)
						{
							member.selectedItems.remove(i--);
							item_member(sitem)->selected = false;
							emit(Signal_ItemSelected, sitem);
						}
					}
					for (int i = min; i <= max; i++)
					{
						BListItem* sitem = member.items[i];
						member.selectedItems.append(sitem);
						item_member(sitem)->selected = true;
						emit(Signal_ItemSelected, sitem);
					}
				}
				emit(Signal_SelectionChanged);
				this->fresh();
			}
		}
		else
		{
			if (item)
			{
				for (int i = 0; i < member.selectedItems.size(); i++)
				{
					BListItem* sitem = member.selectedItems[i];
					if (sitem != item)
					{
						member.selectedItems.remove(i--);
						item_member(sitem)->selected = false;
						emit(Signal_ItemSelected, sitem);
					}
				}
				if (member.selectedItems.empty())
				{
					member.selectedItems.append(item);
					item_member(item)->selected = true;
					emit(Signal_ItemSelected, item);
				}
				emit(Signal_SelectionChanged);
				this->fresh();
			}
			else
			{
				this->clearSelection();
			}
		}
		return;
	}
	if (member.selectMode & Select_Single)
	{
		BListItem* item = member.hoveredItem;
		if (item)
		{
			for (int i = 0; i < member.selectedItems.size(); i++)
			{
				BListItem* sitem = member.selectedItems[i];
				if (sitem != item)
				{
					member.selectedItems.remove(i--);
					item_member(sitem)->selected = false;
					emit(Signal_ItemSelected, sitem);
				}
			}
			if (member.selectedItems.empty())
			{
				member.selectedItems.append(item);
				item_member(item)->selected = true;
				emit(Signal_ItemSelected, item);
			}
			emit(Signal_SelectionChanged);
			this->fresh();
		}
		else
		{
			clearSelection();
		}
	}
	BScrollArea::mousePressEvent(mouseEvent);
}
void BListWidget::mouseReleaseEvent(const BMouseEvent& mouseEvent)
{
	if(member.pressedItem == 0)
	{
		member.pressedItem = 0;
		this->fresh();
	}
	BScrollArea::mouseReleaseEvent(mouseEvent);
}
void BListWidget::mouseMoveEvent(const BMouseEvent& mouseEvent)
{
	const BPoint& pos = mouseEvent.pos();
	BListItem* hoveredItem = this->itemAt(pos);
	if(member.hoveredItem != hoveredItem)
	{
		member.hoveredItem = hoveredItem;
		emit(Signal_ItemHovered, hoveredItem);
		this->fresh();
	}
	BScrollArea::mouseMoveEvent(mouseEvent);
}

void BListWidget::mouseClickEvent(const BMouseEvent& mouseEvent)
{
	if (mouseEvent.button() == Button_Left)
	{
		if (member.pressedItem)
			emit(Signal_ItemClicked, member.pressedItem);
	}
	if (mouseEvent.button() == Button_Right)
	{
		if (member.pressedItem)
			emit(Signal_ItemRightClicked, member.pressedItem);
	}
	BScrollArea::mouseClickEvent(mouseEvent);
}
void BListWidget::mouseDoubleClickEvent(const BMouseEvent& mouseEvent)
{
	if (mouseEvent.button() == Button_Left)
	{
		if (member.pressedItem)
			emit(Signal_ItemDoubleClicked, member.pressedItem);
	}
	BScrollArea::mouseDoubleClickEvent(mouseEvent);
}

